package se.kth.fericss.quicksort;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Test;
/**
 * 
 * @author Fredric
 *
 */
public class QuicksortTest {
	Stopwatch clock = new Stopwatch();
	@Test
	public void testSort() {	
		int k = testK();
		System.out.println("Setting value to "+k);
		Quicksorter[] quick = new Quicksorter[]{
				new QuicksortJava(),
				new QuicksortFixedPivot(),
				new QuicksortRandomPivot(),
				new QuicksortFixedPivotInsertionsort(k),
				new QuicksortRandomPivotInsertionsort(k)};
		System.out.println("Starting the Tests:\n");
		for(Quicksorter qu : quick){
			measure(qu);
		}
	}
	/**
	 * Measures the time to sort the data using the sorter.
	 * Checks that the sorting is accurate.
	 * Prints the results of the measurement to the console.
	 */
	private long measure(Quicksorter sorter) {
		int[] da;
		int[] da2;
		int k;
		long time=0;
		int[] elements = new int[]{1000,10000,100000,1000000};
		System.out.print(""+sorter.getType()+"\nElements:\t1000\t10000\t100000\t1000000");
		Data.Order[] wat = new Data.Order[]{Data.Order.RANDOM, Data.Order.ASCENDING, Data.Order.DESCENDING, Data.Order.SAME_VALUE};
		for(Data.Order enu : wat){//Run once for every enumeration
			System.out.print("\n"+enu.toString()+(enu.toString().length()<8?":\t":""));
			for (int i : elements) {
				Data d = new Data(i,i,enu);
				time=(Integer.MAX_VALUE-1);
				for(k =0;k<10;k++){
					da = d.get();
					da2= d.get();
					Arrays.sort(da2);
					clock.reset().start();
					sorter.sort(da,0,da.length-1);
					long temptime = clock.stop().getTime();
					//skip the first 5 values for more accurate values
					//Set 'time' to the smallest value
					time = k<5? time: temptime<time?temptime:time;
					for(int ik = 0;ik <1000;ik++){//Check that the array has been successfully sorted
						assertTrue(da[ik]==da2[ik]);
					}
				}
				System.out.print("\t"+time+" ms");
			}
		}
		System.out.println("");
		return time;
	}
	/**
	 * A method that tests what k value the quicksorter classes should use.
	 */
	private int testK(){
		QuicksortFixedPivotInsertionsort qu = new QuicksortFixedPivotInsertionsort();
		System.out.println(	"Warning, testing what size of the partition it shuld have when it switches to insertionsort\n" +
							"it should use for best peformence on your computer.\n" +
							"This could take about 1-2 minutes!\n" +
							"Please don't touch your computer while it's testing.\n" +
							"This will ensure that the other tests are more accurate since it varies from computer to computer.");
		int fastest=qu.k;
		long temptime = 10000000;
		int[] k = new int[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
		for(int k2 :k){
			qu.k=k2*5;
			long fas = measureK(qu);
			System.out.println("k="+qu.k+" \tit took: "+fas+" ms");
			if(fas<temptime){
				fastest=qu.k;
				temptime=fas;
			}
		}
		/*
		qu = new QuicksortFixedPivotInsertionsort();
		System.out.println("The optimal k value for your computer would be around the value: "+fastest);
		System.out.println("Please edit the code if it does not match that value.");
		System.out.println("The current k value is set to"+qu.k);
		*/
		return fastest;
	}
	/**
	 * Slightly modified version of the measure method.
	 * @param sorter A quicksorter object
	 * @return The time it took for the quicksorter.
	 */
	private long measureK(Quicksorter sorter) {
		int[] da; 
		long time=0;
		int k;
		int[] elements = new int[]{1000000};
		Data.Order[] wat = new Data.Order[]{Data.Order.RANDOM};
		for(Data.Order enu : wat){
			for (int i : elements) {
				time=(Integer.MAX_VALUE-1);
				Data d = new Data(i,i,enu);
				for(k =0;k<20;k++){
					da = d.get();
					clock.reset().start();
					sorter.sort(da,0,da.length-1);
					long temptime = clock.stop().getTime();
					time = k<5? time: temptime<time?temptime:time;
				}
			}
		}
		return time;
	}
}